home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / ViePratique / ArchiFacile / ArchiFacileSetup.exe / {app} / nw.pak / Unnamed File 000136.txt < prev    next >
Text File  |  2014-10-14  |  14KB  |  427 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. // require: list_selection_model.js
  6. // require: list_selection_controller.js
  7. // require: list.js
  8.  
  9. /**
  10.  * @fileoverview This implements a grid control. Grid contains a bunch of
  11.  * similar elements placed in multiple columns. It's pretty similar to the list,
  12.  * except the multiple columns layout.
  13.  */
  14.  
  15. cr.define('cr.ui', function() {
  16.   /** @const */ var ListSelectionController = cr.ui.ListSelectionController;
  17.   /** @const */ var List = cr.ui.List;
  18.   /** @const */ var ListItem = cr.ui.ListItem;
  19.  
  20.   /**
  21.    * Creates a new grid item element.
  22.    * @param {*} dataItem The data item.
  23.    * @constructor
  24.    * @extends {cr.ui.ListItem}
  25.    */
  26.   function GridItem(dataItem) {
  27.     var el = cr.doc.createElement('li');
  28.     el.dataItem = dataItem;
  29.     el.__proto__ = GridItem.prototype;
  30.     return el;
  31.   }
  32.  
  33.   GridItem.prototype = {
  34.     __proto__: ListItem.prototype,
  35.  
  36.     /**
  37.      * Called when an element is decorated as a grid item.
  38.      */
  39.     decorate: function() {
  40.       ListItem.prototype.decorate.call(this, arguments);
  41.       this.textContent = this.dataItem;
  42.     }
  43.   };
  44.  
  45.   /**
  46.    * Creates a new grid element.
  47.    * @param {Object=} opt_propertyBag Optional properties.
  48.    * @constructor
  49.    * @extends {cr.ui.List}
  50.    */
  51.   var Grid = cr.ui.define('grid');
  52.  
  53.   Grid.prototype = {
  54.     __proto__: List.prototype,
  55.  
  56.     /**
  57.      * The number of columns in the grid. Either set by the user, or lazy
  58.      * calculated as the maximum number of items fitting in the grid width.
  59.      * @type {number}
  60.      * @private
  61.      */
  62.     columns_: 0,
  63.  
  64.     /**
  65.      * Function used to create grid items.
  66.      * @type {function(): !GridItem}
  67.      * @override
  68.      */
  69.     itemConstructor_: GridItem,
  70.  
  71.     /**
  72.      * Whether or not the rows on list have various heights.
  73.      * Shows a warning at the setter because cr.ui.Grid does not support this.
  74.      * @type {boolean}
  75.      */
  76.     get fixedHeight() {
  77.       return true;
  78.     },
  79.     set fixedHeight(fixedHeight) {
  80.       if (!fixedHeight)
  81.         console.warn('cr.ui.Grid does not support fixedHeight = false');
  82.     },
  83.  
  84.     /**
  85.      * @return {number} The number of columns determined by width of the grid
  86.      *     and width of the items.
  87.      * @private
  88.      */
  89.     getColumnCount_: function() {
  90.       // Size comes here with margin already collapsed.
  91.       var size = this.getDefaultItemSize_();
  92.  
  93.       // We should uncollapse margin, since margin isn't collapsed for
  94.       // inline-block elements according to css spec which are thumbnail items.
  95.  
  96.       var width = size.width + Math.min(size.marginLeft, size.marginRight);
  97.       var height = size.height + Math.min(size.marginTop, size.marginBottom);
  98.  
  99.       if (!width || !height)
  100.         return 0;
  101.  
  102.       var itemCount = this.dataModel ? this.dataModel.length : 0;
  103.       if (!itemCount)
  104.         return 0;
  105.  
  106.       var columns = Math.floor(this.clientWidthWithoutScrollbar_ / width);
  107.       if (!columns)
  108.         return 0;
  109.  
  110.       var rows = Math.ceil(itemCount / columns);
  111.       if (rows * height <= this.clientHeight_)
  112.         return columns;
  113.  
  114.       return Math.floor(this.clientWidthWithScrollbar_ / width);
  115.     },
  116.  
  117.     /**
  118.      * Measure and cache client width and height with and without scrollbar.
  119.      * Must be updated when offsetWidth and/or offsetHeight changed.
  120.      */
  121.     updateMetrics_: function() {
  122.       // Check changings that may affect number of columns.
  123.       var offsetWidth = this.offsetWidth;
  124.       var offsetHeight = this.offsetHeight;
  125.       var overflowY = window.getComputedStyle(this).overflowY;
  126.  
  127.       if (this.lastOffsetWidth_ == offsetWidth &&
  128.           this.lastOverflowY == overflowY) {
  129.         this.lastOffsetHeight_ = offsetHeight;
  130.         return;
  131.       }
  132.  
  133.       this.lastOffsetWidth_ = offsetWidth;
  134.       this.lastOffsetHeight_ = offsetHeight;
  135.       this.lastOverflowY = overflowY;
  136.       this.columns_ = 0;
  137.  
  138.       if (overflowY == 'auto' && offsetWidth > 0) {
  139.         // Column number may depend on whether scrollbar is present or not.
  140.         var originalClientWidth = this.clientWidth;
  141.         // At first make sure there is no scrollbar and calculate clientWidth
  142.         // (triggers reflow).
  143.         this.style.overflowY = 'hidden';
  144.         this.clientWidthWithoutScrollbar_ = this.clientWidth;
  145.         this.clientHeight_ = this.clientHeight;
  146.         if (this.clientWidth != originalClientWidth) {
  147.           // If clientWidth changed then previously scrollbar was shown.
  148.           this.clientWidthWithScrollbar_ = originalClientWidth;
  149.         } else {
  150.           // Show scrollbar and recalculate clientWidth (triggers reflow).
  151.           this.style.overflowY = 'scroll';
  152.           this.clientWidthWithScrollbar_ = this.clientWidth;
  153.         }
  154.         this.style.overflowY = '';
  155.       } else {
  156.         this.clientWidthWithoutScrollbar_ = this.clientWidthWithScrollbar_ =
  157.             this.clientWidth;
  158.         this.clientHeight_ = this.clientHeight;
  159.       }
  160.     },
  161.  
  162.     /**
  163.      * The number of columns in the grid. If not set, determined automatically
  164.      * as the maximum number of items fitting in the grid width.
  165.      * @type {number}
  166.      */
  167.     get columns() {
  168.       if (!this.columns_) {
  169.         this.columns_ = this.getColumnCount_();
  170.       }
  171.       return this.columns_ || 1;
  172.     },
  173.     set columns(value) {
  174.       if (value >= 0 && value != this.columns_) {
  175.         this.columns_ = value;
  176.         this.redraw();
  177.       }
  178.     },
  179.  
  180.     /**
  181.      * @param {number} index The index of the item.
  182.      * @return {number} The top position of the item inside the list, not taking
  183.      *     into account lead item. May vary in the case of multiple columns.
  184.      * @override
  185.      */
  186.     getItemTop: function(index) {
  187.       return Math.floor(index / this.columns) * this.getDefaultItemHeight_();
  188.     },
  189.  
  190.     /**
  191.      * @param {number} index The index of the item.
  192.      * @return {number} The row of the item. May vary in the case
  193.      *     of multiple columns.
  194.      * @override
  195.      */
  196.     getItemRow: function(index) {
  197.       return Math.floor(index / this.columns);
  198.     },
  199.  
  200.     /**
  201.      * @param {number} row The row.
  202.      * @return {number} The index of the first item in the row.
  203.      * @override
  204.      */
  205.     getFirstItemInRow: function(row) {
  206.       return row * this.columns;
  207.     },
  208.  
  209.     /**
  210.      * Creates the selection controller to use internally.
  211.      * @param {cr.ui.ListSelectionModel} sm The underlying selection model.
  212.      * @return {!cr.ui.ListSelectionController} The newly created selection
  213.      *     controller.
  214.      * @override
  215.      */
  216.     createSelectionController: function(sm) {
  217.       return new GridSelectionController(sm, this);
  218.     },
  219.  
  220.     /**
  221.      * Calculates the number of items fitting in the given viewport.
  222.      * @param {number} scrollTop The scroll top position.
  223.      * @param {number} clientHeight The height of viewport.
  224.      * @return {{first: number, length: number, last: number}} The index of
  225.      *     first item in view port, The number of items, The item past the last.
  226.      * @override
  227.      */
  228.     getItemsInViewPort: function(scrollTop, clientHeight) {
  229.       var itemHeight = this.getDefaultItemHeight_();
  230.       var firstIndex =
  231.           this.autoExpands ? 0 : this.getIndexForListOffset_(scrollTop);
  232.       var columns = this.columns;
  233.       var count = this.autoExpands_ ? this.dataModel.length : Math.max(
  234.           columns * (Math.ceil(clientHeight / itemHeight) + 1),
  235.           this.countItemsInRange_(firstIndex, scrollTop + clientHeight));
  236.       count = columns * Math.ceil(count / columns);
  237.       count = Math.min(count, this.dataModel.length - firstIndex);
  238.       return {
  239.         first: firstIndex,
  240.         length: count,
  241.         last: firstIndex + count - 1
  242.       };
  243.     },
  244.  
  245.     /**
  246.      * Merges list items. Calls the base class implementation and then
  247.      * puts spacers on the right places.
  248.      * @param {number} firstIndex The index of first item, inclusively.
  249.      * @param {number} lastIndex The index of last item, exclusively.
  250.      * @param {Object.<string, ListItem>} cachedItems Old items cache.
  251.      * @param {Object.<string, ListItem>} newCachedItems New items cache.
  252.      * @override
  253.      */
  254.     mergeItems: function(firstIndex, lastIndex, cachedItems, newCachedItems) {
  255.       List.prototype.mergeItems.call(this,
  256.           firstIndex, lastIndex, cachedItems, newCachedItems);
  257.  
  258.       var afterFiller = this.afterFiller_;
  259.       var columns = this.columns;
  260.  
  261.       for (var item = this.beforeFiller_.nextSibling; item != afterFiller;) {
  262.         var next = item.nextSibling;
  263.         if (isSpacer(item)) {
  264.           // Spacer found on a place it mustn't be.
  265.           this.removeChild(item);
  266.           item = next;
  267.           continue;
  268.         }
  269.         var index = item.listIndex;
  270.         var nextIndex = index + 1;
  271.  
  272.         // Invisible pinned item could be outside of the
  273.         // [firstIndex, lastIndex). Ignore it.
  274.         if (index >= firstIndex && nextIndex < lastIndex &&
  275.             nextIndex % columns == 0) {
  276.           if (isSpacer(next)) {
  277.             // Leave the spacer on its place.
  278.             item = next.nextSibling;
  279.           } else {
  280.             // Insert spacer.
  281.             var spacer = this.ownerDocument.createElement('div');
  282.             spacer.className = 'spacer';
  283.             this.insertBefore(spacer, next);
  284.             item = next;
  285.           }
  286.         } else
  287.           item = next;
  288.       }
  289.  
  290.       function isSpacer(child) {
  291.         return child.classList.contains('spacer') &&
  292.                child != afterFiller;  // Must not be removed.
  293.       }
  294.     },
  295.  
  296.     /**
  297.      * Returns the height of after filler in the list.
  298.      * @param {number} lastIndex The index of item past the last in viewport.
  299.      * @return {number} The height of after filler.
  300.      * @override
  301.      */
  302.     getAfterFillerHeight: function(lastIndex) {
  303.       var columns = this.columns;
  304.       var itemHeight = this.getDefaultItemHeight_();
  305.       // We calculate the row of last item, and the row of last shown item.
  306.       // The difference is the number of rows not shown.
  307.       var afterRows = Math.floor((this.dataModel.length - 1) / columns) -
  308.           Math.floor((lastIndex - 1) / columns);
  309.       return afterRows * itemHeight;
  310.     },
  311.  
  312.     /**
  313.      * Returns true if the child is a list item.
  314.      * @param {Node} child Child of the list.
  315.      * @return {boolean} True if a list item.
  316.      */
  317.     isItem: function(child) {
  318.       // Non-items are before-, afterFiller and spacers added in mergeItems.
  319.       return child.nodeType == Node.ELEMENT_NODE &&
  320.              !child.classList.contains('spacer');
  321.     },
  322.  
  323.     redraw: function() {
  324.       this.updateMetrics_();
  325.       var itemCount = this.dataModel ? this.dataModel.length : 0;
  326.       if (this.lastItemCount_ != itemCount) {
  327.         this.lastItemCount_ = itemCount;
  328.         // Force recalculation.
  329.         this.columns_ = 0;
  330.       }
  331.  
  332.       List.prototype.redraw.call(this);
  333.     }
  334.   };
  335.  
  336.   /**
  337.    * Creates a selection controller that is to be used with grids.
  338.    * @param {cr.ui.ListSelectionModel} selectionModel The selection model to
  339.    *     interact with.
  340.    * @param {cr.ui.Grid} grid The grid to interact with.
  341.    * @constructor
  342.    * @extends {!cr.ui.ListSelectionController}
  343.    */
  344.   function GridSelectionController(selectionModel, grid) {
  345.     this.selectionModel_ = selectionModel;
  346.     this.grid_ = grid;
  347.   }
  348.  
  349.   GridSelectionController.prototype = {
  350.     __proto__: ListSelectionController.prototype,
  351.  
  352.     /**
  353.      * Check if accessibility is enabled: if ChromeVox is running
  354.      * (which provides spoken feedback for accessibility), make up/down
  355.      * behave the same as left/right. That's because the 2-dimensional
  356.      * structure of the grid isn't exposed, so it makes more sense to a
  357.      * user who is relying on spoken feedback to flatten it.
  358.      * @return {boolean} True if accessibility is enabled.
  359.      */
  360.     isAccessibilityEnabled: function() {
  361.       return window.cvox && window.cvox.Api &&
  362.              window.cvox.Api.isChromeVoxActive &&
  363.              window.cvox.Api.isChromeVoxActive();
  364.     },
  365.  
  366.     /**
  367.      * Returns the index below (y axis) the given element.
  368.      * @param {number} index The index to get the index below.
  369.      * @return {number} The index below or -1 if not found.
  370.      * @override
  371.      */
  372.     getIndexBelow: function(index) {
  373.       if (this.isAccessibilityEnabled())
  374.         return this.getIndexAfter(index);
  375.       var last = this.getLastIndex();
  376.       if (index == last)
  377.         return -1;
  378.       index += this.grid_.columns;
  379.       return Math.min(index, last);
  380.     },
  381.  
  382.     /**
  383.      * Returns the index above (y axis) the given element.
  384.      * @param {number} index The index to get the index above.
  385.      * @return {number} The index below or -1 if not found.
  386.      * @override
  387.      */
  388.     getIndexAbove: function(index) {
  389.       if (this.isAccessibilityEnabled())
  390.         return this.getIndexBefore(index);
  391.       if (index == 0)
  392.         return -1;
  393.       index -= this.grid_.columns;
  394.       return Math.max(index, 0);
  395.     },
  396.  
  397.     /**
  398.      * Returns the index before (x axis) the given element.
  399.      * @param {number} index The index to get the index before.
  400.      * @return {number} The index before or -1 if not found.
  401.      * @override
  402.      */
  403.     getIndexBefore: function(index) {
  404.       return index - 1;
  405.     },
  406.  
  407.     /**
  408.      * Returns the index after (x axis) the given element.
  409.      * @param {number} index The index to get the index after.
  410.      * @return {number} The index after or -1 if not found.
  411.      * @override
  412.      */
  413.     getIndexAfter: function(index) {
  414.       if (index == this.getLastIndex()) {
  415.         return -1;
  416.       }
  417.       return index + 1;
  418.     }
  419.   };
  420.  
  421.   return {
  422.     Grid: Grid,
  423.     GridItem: GridItem,
  424.     GridSelectionController: GridSelectionController
  425.   };
  426. });
  427.